.. _глава_69_start:

Битовые операции
================

.. index::
    битовые операции

В этом разделе мы познакомимся с операциями, выполняемыми с битами - наименьшей информационной единицей микропроцессора
с которой мы познакомимся. Операции с битами - одно из самых основных средств микропроцессора.
Эти операции должны быть известны как с точки зрения низкоуровневого программирования, так и
требуется для функций, принимающих флаг в качестве параметра. Функции, принимающие флаг
особенно часто встречаются в библиотеках D, которые адаптированы из библиотек C.

Представление данных на самом низком уровне
-------------------------------------------
Языки программирования - это абстракции. Пользовательский тип типа Student, определенный в
Пользовательский тип, например Student, определенный на языке программирования, не имеет прямого отношения к внутреннему устройству компьютера.
Языки программирования - это инструменты, которые помогают людям использовать оборудование без
без необходимости разбираться в деталях аппаратного обеспечения.
Хотя обычно нет необходимости иметь дело непосредственно с аппаратным обеспечением, полезно
полезно понимать, как данные представляются на аппаратном уровне

**Транзистор**
Вычислител
ьные способности современных электронных устройств в основном основаны на
электронном элементе, называемом транзистором. Важной способностью транзистора является то.
что он может управляться другими частями электронной схемы, в которую входит транзистор.
В некотором смысле это позволяет электронной схеме осознавать себя и быть способной
изменять свое состояние.


.. index::
    бит
    байт
    регистр
    bit
    byte
    register

**Бит**

Наименьшей единицей информации является бит. Бит может быть представлен любой двухсоставной
системой (например, особым расположением нескольких транзисторов электронной схемы).
Бит может иметь одно из двух значений: 0 или 1.В памяти компьютера
информация, хранящаяся в бите, сохраняется до тех пор, пока не будет записано новое значение или пока не отключится источник энергии.
пока источник энергии не будет отключен.
Компьютеры не предоставляют прямого доступа к битам. Одна из причин заключается в том, что это
усложнило бы конструкцию компьютера и, как следствие, сделало бы его более дорогим.
компьютер более дорогим. Другая причина заключается в том, что существует не так много понятий.
которые могут быть представлены одним битом.



**Байт**

Байт - это комбинация из 8 битов. Наименьшая единица информации, которая может быть
однозначно адресовать, является байт. Компьютеры читают из памяти или записывают в нее по крайней мере
по одному байту за раз.
По этой причине, хотя он и несет один бит информации (false или true), дажеbool должен быть реализован как один байт:

.. code-block:: d

    writefln("%s - это %s байт(ов)", bool.stringof, bool.sizeof);

.. code-block:: console

    bool - это 1 байт(ы)

**Регистр**

Запоминающие и обрабатывающие устройства в самом микропроцессоре
регистры. Они обеспечивают очень ограниченные, но очень быстрые операции.
Регистры состоят из некоторого количества байт, которое зависит от разрядности каждого процессора.
Например, в 32-битных процессорах регистры состоят из 4 байт, а в 64-битных - из 8 байт.
состоит из байтов. Размер регистра определяется как возможностями микропроцессора, так и
объемом информации, которую он может обработать, и максимальным объемом памяти, к которой он может обратиться.
определяет.
Каждая задача, выполняемая с помощью языка программирования, в конечном итоге требует одного или
несколько регистров.

Двоичная система счисления
--------------------------

Десятичная система счисления, используемая в повседневной жизни, состоит из 10 цифр:
0123456789. В отличие от нее, двоичная система счисления, используемая в компьютерной технике, состоит из 2 цифр: 0 и 1. Это прямое следствие того, что бит
состоит из двух значений. Если бы биты имели три значения, то компьютеры использовали бы
систему счисления, основанную на трех цифрах.
Цифры десятичной системы называются по возрастанию: единицы, десятки,
сотни, тысячи и т. д. Например, число 1023 можно расширить следующим образом:

.. code-block:: console

    1023 == 1 count of thousand, no hundred, 2 counts of ten, and 3 counts of one

Естественно, перемещение одной цифры влево умножает значение этой цифры на 10: 1, 10,
100, 1000 и т. д.
Если применить те же правила к системе, состоящей из двух цифр, мы получим
к двоичной системе счисления. Цифры называются по возрастанию: единицы, двойки,четверки, восьмерки и т. д. Другими словами, перемещение одной цифры влево умножает
значение этой цифры на 2: 1, 2, 4, 8 и т. д. Например, двоичное число 1011 можно
расширить следующим образом:

.. code-block:: console

    1011 == 1 count of eight, no four, 1 count of two, and 1 count of one

Для удобства обращения к цифрам они нумеруются от крайней правой цифры к
левого, начиная с 0. В следующей таблице перечислены значения всех
всех цифр 32-битного беззнакового числа в двоичной системе:


+-----+------------+
| 31  | 2147483648 |
+-----+------------+
| 30  | 1073741824 |
+-----+------------+
| 29  | 536870912  |
+-----+------------+
| 28  | 268435456  |
+-----+------------+
| 27  | 134217728  |
+-----+------------+
| 26  | 67108864   |
+-----+------------+
| 25  | 33554432   |
+-----+------------+
| 24  | 16777216   |
+-----+------------+
| 23  | 8388608    |
+-----+------------+
| 22  | 4194304    |
+-----+------------+
| 21  | 2097152    |
+-----+------------+
| 20  | 1048576    |
+-----+------------+
| 19  | 524288     |
+-----+------------+
| 18  | 262144     |
+-----+------------+
| 17  | 131072     |
+-----+------------+
| 16  | 65536      |
+-----+------------+
| 15  | 32768      |
+-----+------------+
| 14  | 16384      |
+-----+------------+
| 13  | 8192       |
+-----+------------+
| 12  | 4096       |
+-----+------------+
| 11  | 2048       |
+-----+------------+
| 10  | 1024       |
+-----+------------+
| 9   | 512        |
+-----+------------+
| 8   | 256        |
+-----+------------+
| 7   | 128        |
+-----+------------+
| 6   | 64         |
+-----+------------+
| 5   | 32         |
+-----+------------+
| 4   | 16         |
+-----+------------+
| 3   | 8          |
+-----+------------+
| 2   | 4          |
+-----+------------+
| 1   | 2          |
+-----+------------+
| 0   | 1          |
+-----+------------+

Биты с большими значениями называются старшими битами, а биты с меньшими значениями
меньшие значения, называются младшими битами.
Помня из главы Литералы (:ref:`Литералы<Литералы>`), что двоичные литералы
обозначаются префиксом 0b, следующая программа демонстрирует, как значение литерала
литерала будет соответствовать строкам предыдущей таблицы:

.. code-block:: d

    import std.stdio;

    void main()
    {
    //               1073741824                     4 1
    //               ↓                              ↓ ↓
        int number = 0b_01000000_00000000_00000000_00000101;
        writeln(number);
    }

Вывод

.. code-block:: console

    1073741829

Обратите внимание, что литерал состоит только из трех ненулевых битов. Выводимое значение
это сумма значений, соответствующих этим битам из предыдущей таблицы:

.. code-block:: d

    1073741824 + 4 + 1 == 1073741829

**Знаковый бит знаковых целочисленных типов**

Старший бит знакового типа определяет, является ли значение положительным или
отрицательным

.. code-block:: d

    int number = 0b_10000000_00000000_00000000_00000000;
    writeln(number);

.. code-block:: console

    -2147483648

Однако самый верхний бит не полностью отделен от значения. Например,
как показано выше, тот факт, что все остальные биты числа равны 0, не означает.
не означает, что значение равно -0. (На самом деле -0 не является допустимым значением для целых чисел.) Я не буду
вдаваться в подробности в этой главе, кроме как отмечу, что это связано с представлением двойки
в дополнительном коде, которое используется и в D.
Здесь важно то, что 2 147 483 648, самое большое значение в предыдущей таблице, является допустимым только для беззнаковых целых чисел.
таблице, только для беззнаковых целых типов. Тот же эксперимент с uint
вывел бы именно это значение:

.. code-block:: d

    uint number = 0b_10000000_00000000_00000000_00000000;
    writeln(number);

.. code-block:: console

    2147483648

Отчасти по этой причине, если нет причин не делать этого, битовые операции всегда должны
всегда должны выполняться над беззнаковыми типами: ubyte, ushort, uint и ulong


Шестнадцатеричная система счисления
-----------------------------------

Как видно из приведенных выше литералов, состоящих только из 0 и 1, двоичная
система может оказаться нечитаемой, особенно когда числа большие. По этой
по этой причине была широко распространена более удобная для чтения шестнадцатеричная система.
особенно в компьютерных технологиях.
В шестнадцатеричной системе 16 цифр. Поскольку в алфавитах не бывает больше
10 цифр, эта система заимствует 6 букв из латинского алфавита и использует
их вместе с обычными цифрами: 0123456789abcdef. Числа a, b, c, d, e,
и f имеют значения 10, 11, 12, 13, 14 и 15 соответственно. Буквы ABCDEF
также могут быть использованы.
Как и в других системах счисления, значение каждой цифры в 16 раз больше значения
цифры, стоящей справа от нее: 1, 16, 256, 4096 и т. д. Например, значения
всех цифр 8-значного беззнакового шестнадцатеричного числа следующие:

+-------+-----------+
| Digit | Value     |
+-------+-----------+
| 7     | 268435456 |
+-------+-----------+
| 6     | 16777216  |
+-------+-----------+
| 5     | 1048576   |
+-------+-----------+
| 4     | 65536     |
+-------+-----------+
| 3     | 4096      |
+-------+-----------+
| 2     | 256       |
+-------+-----------+
| 1     | 16        |
+-------+-----------+
| 0     | 1         |
+-------+-----------+

Помня, что шестнадцатеричные литералы обозначаются префиксом 0x, мы можем увидеть
как значения цифр влияют на общее значение числа:

.. code-block:: d

    //           1048576 4096 1
    //                 ↓  ↓  ↓
    uint number = 0x_0030_a00f;
    writeln(number);

.. code-block:: console

    3186703

Значение, которое выводится на печать, складывается из всех ненулевых цифр: 3
1048576, a - 4096 и f - 1. Помня, что a
обозначает 10, а f - 15, значение равно 3145728 + 40960 + 15 == 3186703.
Преобразование между двоичными и шестнадцатеричными числами не представляет сложности. На сайте
чтобы преобразовать шестнадцатеричное число в двоичное, цифры шестнадцатеричного числа
преобразуются в двоичное представление по отдельности. На сайте
Соответствующие представления в трех системах счисления приведены в следующей таблице
следующей таблице:

+-------------+--------+---------+
| Hexadecimal | Binary | Decimal |
+-------------+--------+---------+
| 0           | 0000   | 0       |
+-------------+--------+---------+
| 1           | 0001   | 1       |
+-------------+--------+---------+
| 2           | 0010   | 2       |
+-------------+--------+---------+
| 3           | 0011   | 3       |
+-------------+--------+---------+
| 4           | 0100   | 4       |
+-------------+--------+---------+
| 5           | 0101   | 5       |
+-------------+--------+---------+
| 6           | 0110   | 6       |
+-------------+--------+---------+
| 7           | 0111   | 7       |
+-------------+--------+---------+
| 8           | 1000   | 8       |
+-------------+--------+---------+
| 9           | 1001   | 9       |
+-------------+--------+---------+
| a           | 1010   | 10      |
+-------------+--------+---------+
| b           | 1011   | 11      |
+-------------+--------+---------+
| c           | 1100   | 12      |
+-------------+--------+---------+
| d           | 1101   | 13      |
+-------------+--------+---------+
| e           | 1110   | 14      |
+-------------+--------+---------+
| f           | 1111   | 15      |
+-------------+--------+---------+

Например, шестнадцатеричное число 0x0030a00f можно записать в двоичном
преобразовав его цифры по отдельности в соответствии с предыдущей таблицей:

.. code-block:: d

    / hexadecimal:     0    0    3    0    a    0    0    f
    uint binary = 0b_0000_0000_0011_0000_1010_0000_0000_1111;

Преобразование из двоичной системы в шестнадцатеричную происходит наоборот: Цифры двоичного
преобразуются в шестнадцатеричное представление по четыре цифры за раз.
Например, вот как записать в шестнадцатеричном виде то же двоичное значение, которое мы
использовали ранее:

.. code-block:: d

    // binary:           0100 0000 0000 0000 0000 0000 0000 0101
    uint hexadecimal = 0x___4____0____0____0____0____0____0____5;

Битовые операции
----------------
После того как мы рассмотрели, как значения представляются битами и как числа представляются
Мы рассмотрели операции, которые изменяют значения на битовом уровне.
значения на уровне битов.
Поскольку прямого доступа к отдельным битам нет, даже если эти операции
операции выполняются на уровне битов, они влияют как минимум на 8 битов одновременно. Например, для
переменной типа ubyte битовая операция будет применена ко всем 8 битам этой переменной
переменной.
Поскольку старший бит является знаковым для знаковых типов, я буду игнорировать знаковые типы
и использовать только uint в приведенных ниже примерах. Вы можете повторить эти операции с
ubyte, ushort и ulong; а также byte, short, int и long, если вы
если вы помните о специальном значении старшего бита.
Сначала определим функцию, которая пригодится позже при изучении работы битовых операторов
операторы. Эта функция выводит значение в двоичной, шестнадцатеричной и
десятичной системах:

.. code-block:: d

    import std.stdio;

        void print(uint number)
        {
            writefln("  %032b %08x %10s", number, number, number);
        }

        void main()
        {
            print(123456789);
        }

Вот то же самое значение, выведенное в двоичной, шестнадцатеричной и десятичной системах счисления
системах:

.. code-block:: console

    00000111010110111100110100010101 075bcd15  123456789

.. index::
    ~

**Оператор дополнения: ~ (инверсия)**

Не путайте с двоичным оператором ~, который используется для объединения массивов, это
это унарный оператор ~.
Этот оператор преобразует каждый бит значения в его противоположность: Биты, которые равны 0
становятся 1, а биты, равные 1, становятся 0.

.. code-block:: d

    uint value = 123456789;
    print(value);
    writeln("~ --------------------------------");
    print(~value);

Эффект очевиден в двоичном представлении. Каждый бит был перевернут
(под пунктирной линией):

.. code-block:: console

    0000111010110111100110100010101 075bcd15  123456789
    ~ --------------------------------
    11111000101001000011001011101010 f8a432ea 4171510506

Вот краткое описание работы унарного оператора ~:

.. code-block:: console

    ~0 → 1
    ~1 → 0

**And ооператор: &**

.. index::
    &

& - это бинарный оператор, записываемый между двумя выражениями. Микропроцессор
рассматривает два соответствующих бита двух выражений отдельно от всех
других битов: Биты 31, 30, 29 и т. д. выражений оцениваются отдельно. Значение
значение каждого результирующего бита равно 1, если оба соответствующих бита выражений
равны 1; 0 - в противном случае.

.. code-block:: d

    uint lhs = 123456789;
    uint rhs = 987654321;
    print(lhs);
    print(rhs);
    writeln("& --------------------------------");
    print(lhs & rhs);

Следующий вывод содержит сначала выражение левой части (lhs), а затем
выражение правой части (rhs). Результат операции & находится под
пунктирной линией:

.. code-block:: d

    00000111010110111100110100010101 075bcd15  123456789
    00111010110111100110100010110001 3ade68b1  987654321
    & --------------------------------
    00000010010110100100100000010001 025a4811   39471121

Обратите внимание, что биты результата, имеющие значение 1, - это те биты, в которых
соответствующие биты выражений равны 1.
Этот оператор называется оператором and, потому что он выдает 1, когда оба бита
биты левой и правой частей равны 1. Среди четырех возможных
комбинаций значений 0 и 1, только та, в которой оба значения равны 1
выдает 1:

.. code-block:: console

    0 & 0 → 0
    0 & 1 → 0
    1 & 0 → 0
    1 & 1 → 1

Наблюдения:
* Когда один из битов равен 0, независимо от другого бита результат всегда равен 0.
Соответственно, "and бита на 0" означает очистку этого бита.
* Когда один из битов равен 1, результат равен значению другого бита; анд на 1 не оказывает никакого влияния.
никакого эффекта.

**OR оператор: |**

.. index::
    |

\| - это двоичный оператор, записываемый между двумя выражениями. Микропроцессор
рассматривает два соответствующих бита двух выражений отдельно от всех
других битов. Значение каждого результирующего бита равно 0, если оба соответствующих бита
выражений равны 0; 1 - в противном случае

.. code-block:: d

    uint lhs = 123456789;
    uint rhs = 987654321;
    print(lhs);
    print(rhs);
    writeln("| --------------------------------");
    print(lhs | rhs);

.. code-block:: console

    0000111010110111100110100010101 075bcd15  123456789
    00111010110111100110100010110001 3ade68b1  987654321
    | --------------------------------
    00111111110111111110110110110101 3fdfedb5 1071639989

Обратите внимание, что биты результата, имеющие значение 0, - это те биты, в которых
соответствующие биты выражений равны 0. Когда соответствующий бит в
левой части или правой части равен 1, то результат равен 1:

.. code-block:: console

    0 | 0 → 0
    0 | 1 → 1
    1 | 0 → 1
    1 | 1 → 1

Наблюдения:
* Когда один из битов равен 1, независимо от другого бита результат всегда равен 1.
Соответственно, "перевести бит в 1" означает установить его.
* Когда один из битов равен 0, результат равен значению другого бита; орринг на 0 не оказывает никакого влияния.
никакого эффекта.

**XOR роператор: ^**

.. index::
    ^

Xor - это сокращение от exclusive or. Это также двоичный оператор. Он выдает 1, если
соответствующие биты двух выражений различны:

.. code-block:: d

    uint lhs = 123456789;
    uint rhs = 987654321;
    print(lhs);
    print(rhs);
    writeln("^ --------------------------------");
    print(lhs ^ rhs);

.. code-block:: console

    00000111010110111100110100010101 075bcd15  123456789
    00111010110111100110100010110001 3ade68b1  987654321
    ^ --------------------------------
    00111101100001011010010110100100 3d85a5a4 1032168868

Обратите внимание, что биты результата, имеющие значение 1, - это те биты, в которых
соответствующие биты выражений отличаются друг от друга.

.. code-block:: console

    0 ^ 0 → 0
    0 ^ 1 → 1
    1 ^ 0 → 1
    1 ^ 1 → 0

Наблюдение:
* "Xor bit" с самим собой означает очистить этот бит.
Независимо от своего значения переменная xor сама с собой всегда дает 0:

.. code-block:: d

    uint value = 123456789;
    print(value ^ value);

.. code-block:: console

    00000000000000000000000000000000 00000000          0

.. index::
    >>
    >>>
    <<

**Оператор правого сдвига: >>**

Этот оператор сдвигает биты выражения на указанное количество бит вправо.
вправо. Крайние правые биты, в которые нет места для сдвига, выпадают
из значения. Для беззнаковых типов крайние левые биты заполняются нулями.
Следующий пример выдает результат, сдвигая значение на два бита вправо
вправо:

.. code-block:: d

     uint value = 123456789;
     print(value);
     print(value >> 2);

В следующем выводе я выделил как биты, которые будут потеряны из-за
выпадения из правой части, и самые левые биты, которые получат значение 0:

.. code-block:: console

    00000111010110111100110100010101 075bcd15  123456789
    00000001110101101111001101000101 01d6f345   30864197

Обратите внимание, что биты, которые не выделены, были сдвинуты на две битовые позиции вправо.
вправо.
Новые биты, которые входят с левой стороны, равны 0 только для беззнаковых типов.
Для знаковых типов значение крайних левых битов определяется процессом, называемым
расширением знака. Расширение знака сохраняет значение знакового бита исходного
выражения. Значение этого бита используется для всех битов, входящих слева.
Рассмотрим этот эффект для значения знакового типа, в котором бит знака равен 1 (т. е. значение
значение отрицательное):

.. code-block:: d

    int value = 0x80010300;
    print(value);
    print(value >> 3);

Поскольку крайний левый бит исходного значения равен 1, все новые биты результата
также равны 1:

.. code-block:: console

    10000000000000010000001100000000 80010300 2147549952
    11110000000000000010000001100000 f0002060 4026540128

Если крайний левый бит равен 0, то все новые биты равны 0

.. code-block:: d

    int value = 0x40010300;
    print(value);
    print(value >> 3);

.. code-block:: console

    01000000000000010000001100000000 40010300 1073808128
    00001000000000000010000001100000 08002060  134226016

**Беззнаковый оператор правого сдвига: >>>**

Этот оператор работает так же, как и обычный оператор правого сдвига. Разница в том.
что новые крайние левые биты всегда равны 0, независимо от типа выражения
и значения крайнего левого бита:

.. code-block:: d

    int value = 0x80010300;
    print(value);
    print(value >>> 3);

.. code-block:: console

    10000000000000010000001100000000 80010300 2147549952
    00010000000000000010000001100000 10002060 268443744

**Оператор левого сдвига: <<**

Этот оператор работает как обратный оператору правого сдвига. Биты сдвигаются
влево:

.. code-block:: d

    uint value = 123456789;
    print(value);
    print(value << 4);

Биты в левой части теряются, а в правой части появляются новые биты 0:

.. code-block:: console

    00000111010110111100110100010101 075bcd15 123456789
    01110101101111001101000101010000 75bcd150 1975308624

**Операторы с присваиванием**

Все приведенные выше бинарные операторы имеют аналоги с присваиванием: \&=, \|=, ^=, >>=,
>>>= и <<=. Подобно операторам, которые мы рассматривали в главе "Целые числа и
Арифметические операции (:ref:`Целочисленная арифметика<Целочисленная арифметика>`), эти операторы присваивают результат обратно к
левому операнду.
Рассмотрим это на примере оператора &=:

.. code-block:: d

    value = value & 123;
    value &= 123; // то же самое, что и выше

Семантика
---------

Простого понимания того, как работают эти операторы на уровне битов, может быть недостаточно.
чтобы понять, как они полезны в программах. В следующих разделах описаны распространенные
как эти операторы используются.

**\| - это объединение множеств**

Оператор \| дает объединение битов 1 в двух выражениях.
В качестве экстремального примера рассмотрим два значения, у которых оба бита поочередно
равны 1. Объединение этих значений даст результат, в котором все биты равны 1

.. code-block:: d

    uint lhs = 0xaaaaaaaa;
    uint rhs = 0x55555555;
    print(lhs);
    print(rhs);
    writeln("| --------------------------------");
    print(lhs | rhs);

.. code-block:: console

    10101010101010101010101010101010 aaaaaaaa 2863311530
    01010101010101010101010101010101 55555555 1431655765
    | --------------------------------
    11111111111111111111111111111111 ffffffff 4294967295


**& - это пересечение множеств**

Оператор & дает пересечение битов 1 в двух выражениях.
В качестве экстремального примера давайте снова рассмотрим два последних значения. Поскольку ни один из
битов 1 в предыдущих двух выражениях не совпадает с битами в другом выражении,
все биты результата равны 0:

.. code-block:: d

    uint lhs = 0xaaaaaaaa;
    uint rhs = 0x55555555;
    print(lhs);
    print(rhs);
    writeln("& --------------------------------");
    print(lhs & rhs);

.. code-block:: console

     10101010101010101010101010101010 aaaaaaaa 2863311530
     01010101010101010101010101010101 55555555 1431655765
     & --------------------------------
     00000000000000000000000000000000 00000000          0

**\|= устанавливает выбранные биты в 1**

Чтобы понять, как это работает, нужно представить одно из выражений как фактическое
а другое - как селектор битов, которые нужно установить в 1:

.. code-block:: d

     uint expression = 0x00ff00ff;
     uint bitsToSet = 0x10001000;
     write("before :"); print(expression);
     write("to set to 1: "); print(bitsToSet);
     expression |= bitsToSet;
     write("after      :"); print(expression);


Значения до и после для битов, на которые оказывается влияние, выделены:

.. code-block:: console

    до : 00000000111111110000000011111111 00ff00ff 16711935
    установить в 1: 00010000000000000001000000000000 10001000 268439552
    после : 00010000111111110001000011111111 10ff10ff 285151487

В некотором смысле, bitsToSet определяет, какие биты следует установить в 1. Остальные биты не затрагиваются
не затрагиваются.


**&= очищает выбранные биты**

Одно из выражений можно рассматривать как действительное выражение, а другое
можно рассматривать как селектор битов, которые нужно очистить (установить в 0):

.. code-block:: d

    uint expression = 0x00ff00ff;
    uint bitsToClear = 0xffefffef;
    write("before       :"); print(expression);
    write("bits to clear:"); print(bitsToClear);
    expression &= bitsToClear;
    write("after        :"); print(expression);

Выделены значения до и после для битов, на которые оказывается воздействие:

.. code-block:: console

    до : 00000000111111110000000011111111 00ff00ff 16711935
    биты для очистки: 11111111111011111111111111101111 ffefffef 4293918703
    после : 00000000111011110000000011101111 00ef00ef 15663343


В некотором смысле, bitsToClear определяет, какие биты следует установить в 0. Остальные биты не затрагиваются

**& определяет, равен ли бит 1 или нет.**

Если в одном из выражений только один бит установлен в 1, то его можно использовать для запроса
равен ли 1 соответствующий бит другого выражения:

.. code-block:: d

    uint expression = 123456789;
    uint bitToQuery = 0x00010000;
    print(expression);
    print(bitToQuery);
    writeln(expression & bitToQuery ? "да, 1" : "не 1");

Бит, который запрашивается, выделяется:

.. code-block:: console

    00000111010110111100110100010101 075bcd15 123456789
    00000000000000010000000000000000 00010000 65536
    да, 1

Давайте запросим еще один бит того же выражения, на этот раз установив для другого бита
bitToQuery установлен в 1:

.. code-block:: d

    uint bitToQuery = 0x00001000;

.. code-block:: console

     00000111010110111100110100010101 075bcd15 123456789
     00000000000000000001000000000000 00001000 4096
     не 1

Если в выражении запроса более чем один бит установлен в 1, то запрос должен
определить, равен ли 1 любой из соответствующих битов в другом выражении.

**Сдвиг вправо на единицу эквивалентен делению на два**

Сдвиг всех битов значения на одну позицию вправо дает половину
исходного значения. Причину этого можно увидеть в таблице цифровых значений выше:
В этой таблице каждый бит имеет половину значения того бита, который находится слева от него.
Сдвиг значения вправо на несколько битов за раз означает деление на 2 для этого
столько раз. Например, сдвиг вправо на 3 бита разделит значение на 8:

.. code-block:: d

    uint value = 8000;
    writeln(value >> 3);

.. code-block:: console

    1000

Согласно принципу работы системы двойных дополнений, сдвиг вправо имеет такой же эффект
эффект для знаковых значений:

.. code-block:: d

   int value = -8000;
   writeln(value >> 3);

.. code-block:: console

   -1000

**Сдвиг влево на единицу эквивалентен умножению на два**

Поскольку каждый бит в два раза больше значения бита справа от него, сдвиг значения на один
сдвиг значения на один бит влево означает умножение этого значения на два:

.. code-block:: d

   uint value = 10;
   writeln(value << 5);

Умножение на 2 в общей сложности 5 раз равнозначно умножению на 32: 320

Общие случаи использования
--------------------------

**Флаги**

Флаги - это однобитные независимые данные, которые хранятся вместе в одной переменной.
Поскольку ширина каждого из них составляет всего один бит, они подходят для представления двоичных
таких понятий, как включен/выключен, действителен/недействителен и т. д. Такие однобитные понятия иногда встречаются в модулях D, которые основаны
на библиотеках C.
Флаги обычно определяются как непересекающиеся значения типа enum.
В качестве примера рассмотрим гоночную игру, в которой реалистичность игры
настраивается:
* Расход топлива реалистичен или нет.
* Столкновения могут повреждать автомобили или нет.
* Шины могут разрушаться от использования или нет.
* Следы от колей остаются на дорожном покрытии или нет.

Эти параметры конфигурации могут быть заданы во время выполнения с помощью следующего перечисления
значения:

.. code-block:: d

    enum Realism {
      fuelUse    = 1 << 0,
      bodyDamage = 1 << 1,
      tireUse    = 1 << 2,
      skidMarks  = 1 << 3
    }

Обратите внимание, что все эти значения состоят из отдельных битов, которые не противоречат друг другу
друг с другом. Каждое значение определяется сдвигом 1 влево на разное количество битов.
Соответствующие битовые представления выглядят следующим образом:


.. code-block:: console

    fuelUse   : 0001
    bodyDamage: 0010
    tireUse   : 0100
    skidMarks : 1000

Поскольку их 1 бит не совпадает с другими, эти значения могут быть объединены оператором |
чтобы сохранить их в одной переменной. Например, два параметра конфигурации
относящиеся к шинам, можно объединить, как показано в следующем коде:

.. code-block:: d

    Realism flags = Realism.tireUse | Realism.skidMarks;
    writefln("%b", flags);

Биты этих двух флагов будут находиться рядом в переменной flags:

.. code-block:: console

   1100

Позже эти флаги можно будет запросить с помощью оператора &:

.. code-block:: d

    if (flags & Realism.fuelUse) {
       // ... code related to fuel consumption ...
       }
       if (flags & Realism.tireUse) {
       // ... code related to tire consumption ...
    }

Оператор & выдает 1 только в том случае, если указанный флаг установлен во флажках.
Также обратите внимание, что результат можно использовать в условии if благодаря автоматическому
преобразования ненулевого значения в true. Условное выражение будет ложным
если результат & равен 0, и истинно в противном случае. В результате соответствующий блок кода
выполняется только в том случае, если флаг включен.

**Маски**

В некоторых библиотеках и протоколах целочисленное значение может нести более одной
информации. Например, старшие 3 бита 32-битного значения могут иметь
определенное значение, в то время как младшие 29 бит могут иметь другое значение. Эти
Эти отдельные части данных могут быть извлечены из переменной с помощью маскирования.
Примером такой концепции являются четыре октета адреса IPv4. Октеты
это отдельные значения, которые составляют общее точечное представление
IPv4-адреса. Все они хранятся в одном 32-битном значении. Например, IPv4
адрес 192.168.1.2 - это 32-битное значение 0xc0a80102:

.. code-block:: console

    c0 == 12 * 16 + 0 = 192
    a8 == 10 * 16 + 8 = 168
    01 ==  0 * 16 + 1 =   1
    02 ==  0 * 16 + 2 =   2

Маска состоит из нескольких битов 1, которые покрывают определенную часть переменной
переменной. "И" в значении по маске извлекает часть переменной, которая
часть переменной, которая покрывается этой маской. Например, значение маски 0x000000ff покроет
младшие 8 бит значения:

.. code-block:: d

    uint value = 123456789;
    uint mask  = 0x000000ff;
    write("value :"); print(value);
    write("mask  :"); print(mask);
    write("result:"); print(value & mask);

Биты, на которые распространяется действие маски, выделены. Все остальные биты
очищены:

.. code-block:: console

    value :  00000111010110111100110100010101 075bcd15  123456789
    mask  :  00000000000000000000000011111111 000000ff        255
    result:  00000000000000000000000000010101 00000015         21

Применим тот же метод к IPv4-адресу 0xc0a80102 с маской, которая
которая покрывает 8 верхних битов:

.. code-block:: d

    uint value = 0xc0a80102;
    uint mask  = 0xff000000;
    write("value :"); print(value);
    write("mask  :"); print(mask);
    write("result:"); print(value & mask);

Эта маска охватывает старшие 8 бит значения:

.. code-block:: console

    value : 11000000101010000000000100000010 c0a80102 3232235778
    mask  :  11111111000000000000000000000000 ff000000 4278190080
    result: 11000000000000000000000000000000 c0000000 3221225472

Однако обратите внимание, что напечатанный результат - не ожидаемые 192, а 3221225472. Это
потому, что маскируемое значение также должно быть сдвинуто до упора в правую
сторону. Если сдвинуть значение на 24 битовые позиции вправо, то получится значение, которое
которое представляют эти 8 битов:

.. code-block:: d

    uint value = 0xc0a80102;
    uint mask = 0xff000000;
    write("value :"); print(value);
    write("mask :"); print(mask);
    write("result:"); print((value & mask) >> 24);

.. code-block:: console

    значение : 11000000101010000000000100000010 c0a80102 3232235778
    маска : 11111111000000000000000000000000 ff000000 4278190080
    результат: 00000000000000000000000011000000 000000c0 192

Упражнения
-----------

1. Напишите функцию, которая возвращает IPv4-адрес в его точечной форме:

.. code-block:: d

    string dotted(uint address) {
    // ...
    }

    unittest {
      assert(dotted(0xc0a80102) == "192.168.1.2");
    }

2. Напишите функцию, которая преобразует четыре октетных значения в соответствующий 32-битный
IPv4-адрес:

.. code-block::

    uint ipAddress(ubyte octet3, // most significant octet
    ubyte octet2,
    ubyte octet1,
    ubyte octet0) { // least significant octet
    // ...
    }

    unittest {
      assert(ipAddress(192, 168, 1, 2) == 0xc0a80102);
    }

3. Напишите функцию, которая может быть использована для создания маски. Она должна начинаться с
с указанного бита и иметь указанную ширину:

.. code-block:: d

    uint mask(int lowestBit, int width) {
    // ...
    }

    unittest {
       assert(mask(2, 5) ==
       0b_0000_0000_0000_0000_0000_0000_0111_1100);
       //                                            ↑
       //                              lowest bit is 2,
       //                              and the mask is 5-bit wide
    }
